﻿#region COPYRIGHT© 2005-2013 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;

namespace FlitBit.Parse
{
	/// <summary>
	/// String and character sequence extensions for parsing.
	/// </summary>
	public static class Extensions
	{
		/// <summary>
		/// Determines if the character at the cursor belongs to the unicode category.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="category">the unicode category</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool At(this string self, int cursor, UnicodeCategory category)
		{
			if (self == null || self.Length <= cursor)
			{
				return false;
			}
			return Char.GetUnicodeCategory(self[cursor]) == category;
		}

		/// <summary>
		/// Determines if the character at the cursor equals a character value.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="value">the character value</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool At(this string self, int cursor, char value)
		{
			if (self == null || self.Length <= cursor)
			{
				return false;
			}
			return self[cursor] == value;
		}

		/// <summary>
		/// Determines if the cursor is positioned at a character sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="value">the character value</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool At(this string self, int cursor, params char[] value)
		{
			if (self == null || cursor >= self.Length)
			{
				return false;
			}

			#region Unroll the comparison loop up to character length of 10

			switch (value.Length)
			{
				case 1:
					return self[cursor] == value[0];
				case 2:
					return (cursor + 1) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1];
				case 3:
					return (cursor + 2) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2];
				case 4:
					return (cursor + 3) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3];
				case 5:
					return (cursor + 4) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4];
				case 6:
					return (cursor + 5) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5];
				case 7:
					return (cursor + 6) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6];
				case 8:
					return (cursor + 7) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7];
				case 9:
					return (cursor + 8) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8];
				case 10:
					return (cursor + 9) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8]
						&& self[cursor + 9] == value[9];
				default:
					if ((cursor + (value.Length - 1)) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8]
						&& self[cursor + 9] == value[9]
						&& self[cursor + 10] == value[10])
					{
						for (var i = 11; i < value.Length; i++)
						{
							if (self[cursor + i] != value[i])
							{
								return false;
							}
						}
						return true;
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at a character sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="value">the character value</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool At(this string self, int cursor, string value)
		{
			if (self == null || cursor >= self.Length)
			{
				return false;
			}

			#region Unroll the comparison loop up to character length of 10

			switch (value.Length)
			{
				case 1:
					return self[cursor] == value[0];
				case 2:
					return (cursor + 1) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1];
				case 3:
					return (cursor + 2) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2];
				case 4:
					return (cursor + 3) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3];
				case 5:
					return (cursor + 4) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4];
				case 6:
					return (cursor + 5) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5];
				case 7:
					return (cursor + 6) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6];
				case 8:
					return (cursor + 7) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7];
				case 9:
					return (cursor + 8) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8];
				case 10:
					return (cursor + 9) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8]
						&& self[cursor + 9] == value[9];
				default:
					if ((cursor + (value.Length - 1)) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8]
						&& self[cursor + 9] == value[9]
						&& self[cursor + 10] == value[10])
					{
						for (var i = 11; i < value.Length; i++)
						{
							if (self[cursor + i] != value[i])
							{
								return false;
							}
						}
						return true;
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at a character sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="value">the character value</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		
		public static bool At(this char[] self, int cursor, params char[] value)
		{
			if (self == null || cursor >= self.Length)
			{
				return false;
			}

			#region Unroll the comparison loop up to character length of 10

			switch (value.Length)
			{
				case 1:
					return self[cursor] == value[0];
				case 2:
					return (cursor + 1) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1];
				case 3:
					return (cursor + 2) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2];
				case 4:
					return (cursor + 3) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3];
				case 5:
					return (cursor + 4) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4];
				case 6:
					return (cursor + 5) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5];
				case 7:
					return (cursor + 6) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6];
				case 8:
					return (cursor + 7) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7];
				case 9:
					return (cursor + 8) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8];
				case 10:
					return (cursor + 9) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8]
						&& self[cursor + 9] == value[9];
				default:
					if ((cursor + (value.Length - 1)) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8]
						&& self[cursor + 9] == value[9]
						&& self[cursor + 10] == value[10])
					{
						for (var i = 11; i < value.Length; i++)
						{
							if (self[cursor + i] != value[i])
							{
								return false;
							}
						}
						return true;
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at a character sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="value">the character value</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool At(this char[] self, int cursor, string value)
		{
			if (self == null || cursor >= self.Length)
			{
				return false;
			}

			#region Unroll the comparison loop up to character length of 10

			switch (value.Length)
			{
				case 1:
					return self[cursor] == value[0];
				case 2:
					return (cursor + 1) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1];
				case 3:
					return (cursor + 2) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2];
				case 4:
					return (cursor + 3) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3];
				case 5:
					return (cursor + 4) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4];
				case 6:
					return (cursor + 5) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5];
				case 7:
					return (cursor + 6) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6];
				case 8:
					return (cursor + 7) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7];
				case 9:
					return (cursor + 8) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8];
				case 10:
					return (cursor + 9) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8]
						&& self[cursor + 9] == value[9];
				default:
					if ((cursor + (value.Length - 1)) < self.Length
						&& self[cursor] == value[0]
						&& self[cursor + 1] == value[1]
						&& self[cursor + 2] == value[2]
						&& self[cursor + 3] == value[3]
						&& self[cursor + 4] == value[4]
						&& self[cursor + 5] == value[5]
						&& self[cursor + 6] == value[6]
						&& self[cursor + 7] == value[7]
						&& self[cursor + 8] == value[8]
						&& self[cursor + 9] == value[9]
						&& self[cursor + 10] == value[10])
					{
						for (var i = 11; i < value.Length; i++)
						{
							if (self[cursor + i] != value[i])
							{
								return false;
							}
						}
						return true;
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at a character sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="value">the character value</param>
		/// <param name="comparison">a string comparison</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool At(this string self, int cursor, string value, StringComparison comparison)
		{
			if (self == null
				|| value == null || value.Length == 0
				|| self.Length <= (cursor + value.Length))
			{
				return false;
			}
			return String.Equals(self.Substring(cursor, value.Length), value, comparison);
		}

		/// <summary>
		/// Determines if the cursor is positioned at any of the characters given in the sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="values">the character values</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtAny(this string self, int cursor, params char[] values)
		{
			if (self == null || self.Length <= cursor
				|| values == null || values.Length == 0)
			{
				return false;
			}

			var c = self[cursor];

			#region Switch ch.Length

			switch (values.Length)
			{
				case 1:
					return c == values[0];
				case 2:
					return c == values[0]
						|| c == values[1];
				case 3:
					return c == values[0]
						|| c == values[1]
						|| c == values[2];
				case 4:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3];
				case 5:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4];
				case 6:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5];
				case 7:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6];
				case 8:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7];
				case 9:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8];
				case 10:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8]
						|| c == values[9];
				default:
					if (c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8]
						|| c == values[9])
					{
						return true;
					}
					for (var i = 10; i < values.Length; i++)
					{
						if (c == values[i])
						{
							return true;
						}
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at any of the characters given in the sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="values">the character values</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtAny(this string self, int cursor, string values)
		{
			if (values == null)
			{
				throw new ArgumentNullException("values");
			}
			if (self == null || self.Length <= cursor || values.Length == 0)
			{
				return false;
			}

			var c = self[cursor];

			#region Switch ch.Length

			switch (values.Length)
			{
				case 1:
					return c == values[0];
				case 2:
					return c == values[0]
						|| c == values[1];
				case 3:
					return c == values[0]
						|| c == values[1]
						|| c == values[2];
				case 4:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3];
				case 5:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4];
				case 6:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5];
				case 7:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6];
				case 8:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7];
				case 9:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8];
				case 10:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8]
						|| c == values[9];
				default:
					if (c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8]
						|| c == values[9])
					{
						return true;
					}
					for (var i = 10; i < values.Length; i++)
					{
						if (c == values[i])
						{
							return true;
						}
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at any of the characters given in the sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="values">the character values</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtAny(this char[] self, int cursor, params char[] values)
		{
			if (self == null || self.Length <= cursor
				|| values == null || values.Length == 0)
			{
				return false;
			}

			var c = self[cursor];

			#region Switch ch.Length

			switch (values.Length)
			{
				case 1:
					return c == values[0];
				case 2:
					return c == values[0]
						|| c == values[1];
				case 3:
					return c == values[0]
						|| c == values[1]
						|| c == values[2];
				case 4:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3];
				case 5:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4];
				case 6:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5];
				case 7:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6];
				case 8:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7];
				case 9:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8];
				case 10:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8]
						|| c == values[9];
				default:
					if (c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8]
						|| c == values[9])
					{
						return true;
					}
					for (var i = 10; i < values.Length; i++)
					{
						if (c == values[i])
						{
							return true;
						}
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at any of the characters given in the sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="values">the character values</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtAny(this char[] self, int cursor, string values)
		{
			if (values == null)
			{
				throw new ArgumentNullException("values");
			}
			if (self == null || self.Length <= cursor || values.Length == 0)
			{
				return false;
			}

			var c = self[cursor];

			#region Switch ch.Length

			switch (values.Length)
			{
				case 1:
					return c == values[0];
				case 2:
					return c == values[0]
						|| c == values[1];
				case 3:
					return c == values[0]
						|| c == values[1]
						|| c == values[2];
				case 4:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3];
				case 5:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4];
				case 6:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5];
				case 7:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6];
				case 8:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7];
				case 9:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8];
				case 10:
					return c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8]
						|| c == values[9];
				default:
					if (c == values[0]
						|| c == values[1]
						|| c == values[2]
						|| c == values[3]
						|| c == values[4]
						|| c == values[5]
						|| c == values[6]
						|| c == values[7]
						|| c == values[8]
						|| c == values[9])
					{
						return true;
					}
					for (var i = 10; i < values.Length; i++)
					{
						if (c == values[i])
						{
							return true;
						}
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at any of the strings given in the sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="values">the string values</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtAny(this string self, int cursor, params string[] values)
		{
			if (self == null || values == null || values.Length == 0)
			{
				return false;
			}

			#region Switch values.Length

			switch (values.Length)
			{
				case 1:
					return At(self, cursor, values[0]);
				case 2:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1]);
				case 3:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2]);
				case 4:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3]);
				case 5:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4]);
				case 6:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5]);
				case 7:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6]);
				case 8:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6])
						|| At(self, cursor, values[7]);
				case 9:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6])
						|| At(self, cursor, values[7])
						|| At(self, cursor, values[8]);
				case 10:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6])
						|| At(self, cursor, values[7])
						|| At(self, cursor, values[8])
						|| At(self, cursor, values[9]);
				default:
					if (At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6])
						|| At(self, cursor, values[7])
						|| At(self, cursor, values[8])
						|| At(self, cursor, values[9])
						|| At(self, cursor, values[10]))
					{
						return true;
					}
					for (var i = 11; i < values.Length; i++)
					{
						if (At(self, cursor, values[i]))
						{
							return true;
						}
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at any of the strings given in the sequence.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <param name="values">the string values</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtAny(this char[] self, int cursor, params string[] values)
		{
			if (self == null || values == null || values.Length == 0)
			{
				return false;
			}

			#region Switch values.Length

			switch (values.Length)
			{
				case 1:
					return At(self, cursor, values[0]);
				case 2:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1]);
				case 3:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2]);
				case 4:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3]);
				case 5:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4]);
				case 6:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5]);
				case 7:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6]);
				case 8:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6])
						|| At(self, cursor, values[7]);
				case 9:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6])
						|| At(self, cursor, values[7])
						|| At(self, cursor, values[8]);
				case 10:
					return At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6])
						|| At(self, cursor, values[7])
						|| At(self, cursor, values[8])
						|| At(self, cursor, values[9]);
				default:
					if (At(self, cursor, values[0])
						|| At(self, cursor, values[1])
						|| At(self, cursor, values[2])
						|| At(self, cursor, values[3])
						|| At(self, cursor, values[4])
						|| At(self, cursor, values[5])
						|| At(self, cursor, values[6])
						|| At(self, cursor, values[7])
						|| At(self, cursor, values[8])
						|| At(self, cursor, values[9])
						|| At(self, cursor, values[10]))
					{
						return true;
					}
					for (var i = 11; i < values.Length; i++)
					{
						if (At(self, cursor, values[i]))
						{
							return true;
						}
					}
					break;
			}

			#endregion

			return false;
		}

		/// <summary>
		/// Determines if the cursor is positioned at a line terminator.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtLineTerminator(this string self, Cursor cursor)
		{
			return AtAny(self, cursor, Constants.LineTerminatorChars);
		}

		/// <summary>
		/// Determines if the cursor is positioned at a line terminator.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtLineTerminator(this char[] self, Cursor cursor)
		{
			return AtAny(self, cursor, Constants.LineTerminatorChars);
		}

		/// <summary>
		/// Determines if the cursor is positioned at a whitespace character.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtWhitespace(this string self, Cursor cursor)
		{
			return AtAny(self, cursor, Constants.WhitespaceChars);
		}

		/// <summary>
		/// Determines if the cursor is positioned at a whitespace character.
		/// </summary>
		/// <param name="self">the input sequence</param>
		/// <param name="cursor">the cursor position</param>
		/// <returns><em>true</em> if the cursor position matches; otherwise <em>false</em>.</returns>
		public static bool AtWhitespace(this char[] self, Cursor cursor)
		{
			return AtAny(self, cursor, Constants.WhitespaceChars);
		}

		/// <summary>
		/// Captures the first occurrence of a sequence matching the given rule.
		/// </summary>
		/// <param name="input">the input</param>
		/// <param name="rule">a match rule</param>
		/// <returns>The first captured sequence matching the rule; otherwise <see cref="Capture.Invalid"/>.</returns>
		public static Capture CaptureFirst(this string input, Match rule)
		{
			Capture capture;
			var c = new Cursor(0);
			while (c < input.Length)
			{
				if (rule.TryCapture(input, ref c, out capture))
				{
					return capture;
				}
				c++;
			}
			return Capture.Invalid;
		}

		/// <summary>
		/// Captures the next occurrence of a sequence matching the given rule.
		/// </summary>
		/// <param name="input">the input</param>
		/// <param name="rule">a match rule</param>
		/// <param name="cursor">a cursor position</param>
		/// <returns>The next captured sequence matching the rule; otherwise <see cref="Capture.Invalid"/>.</returns>
		/// <remarks>Beginning at the initial <paramref name="cursor"/> position, the algorithm proceeds to the end of input,
		/// matching the next sequence satisfying the provide match <paramref name="rule"/>. If another match is found (captured), 
		/// the <paramref name="cursor"/> is advanced to the first input character beyond the captured sequence.
		/// </remarks>
		public static Capture CaptureNext(this string input, Match rule, ref Cursor cursor)
		{
			Capture capture;
			var c = cursor;
			while (c < input.Length)
			{
				if (rule.TryCapture(input, ref c, out capture))
				{
					cursor = c;
					return capture;
				}
				c++;
			}
			return Capture.Invalid;
		}

		/// <summary>
		/// Wraps the string in double quotes, delimiting any double quotes already present in the string.
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		public static string DoubleQuote(this string value)
		{
			Contract.Requires(value != null);
			return Quote(value, '"');
		}

		/// <summary>
		/// Examines whether the cursor is positioned at a particular character; throwing a ParseException otherwise.
		/// </summary>
		/// <param name="self"></param>
		/// <param name="cursor"></param>
		/// <param name="value"></param>
		/// <exception cref="ParseException">thrown if the cursor is not positioned at the sequence given.</exception>
		/// <returns></returns>
		public static char ExpectAt(this string self, ref Cursor cursor, char value)
		{
			if (!At(self, cursor, value))
			{
				throw ParseException.MakeFromExpected(cursor, value.ToString(CultureInfo.InvariantCulture));
			}

			return self[cursor++];
		}

		/// <summary>
		/// Examines whether the cursor is positioned at a particular character sequence; throwing a ParseException otherwise.
		/// </summary>
		/// <param name="self"></param>
		/// <param name="cursor"></param>
		/// <param name="sequence"></param>
		/// <exception cref="ParseException">thrown if the cursor is not positioned at the sequence given.</exception>
		/// <returns></returns>
		public static char[] ExpectAt(this string self, ref Cursor cursor, params char[] sequence)
		{
			if (!At(self, cursor, sequence))
			{
				throw ParseException.MakeFromExpected(cursor, sequence.ToString());
			}

			cursor += sequence.Length;
			return sequence;
		}

		/// <summary>
		/// Examines if the cursor is positioned at a particular string value; throwing a ParseException otherwise.
		/// </summary>
		/// <param name="self"></param>
		/// <param name="cursor"></param>
		/// <param name="value"></param>
		/// <exception cref="ParseException">thrown if the cursor is not positioned at the sequence given.</exception>
		/// <returns></returns>
		public static string ExpectAt(this string self, ref Cursor cursor, string value)
		{
			if (!At(self, cursor, value))
			{
				throw ParseException.MakeFromExpected(cursor, value);
			}

			cursor += value.Length;
			return value;
		}

		/// <summary>
		/// Examines if the cursor is positioned at a particular string value; throwing a ParseException otherwise.
		/// </summary>
		/// <param name="self"></param>
		/// <param name="cursor"></param>
		/// <param name="value"></param>
		/// <param name="comparison"></param>
		/// <exception cref="ParseException">thrown if the cursor is not positioned at the sequence given.</exception>
		/// <returns></returns>
		public static string ExpectAt(this string self, ref Cursor cursor, string value, StringComparison comparison)
		{
			if (!At(self, cursor, value, comparison))
			{
				throw ParseException.MakeFromExpected(cursor, value);
			}

			cursor += value.Length;
			return value;
		}

		/// <summary>
		/// Examines if the cursor is positioned at one of a set of character values; throwing a ParseException otherwise.
		/// </summary>
		/// <param name="self"></param>
		/// <param name="cursor"></param>
		/// <param name="values"></param>
		/// <exception cref="ParseException">thrown if the cursor is not positioned at one of the characters given.</exception>
		/// <returns>the matching character</returns>
		public static char ExpectAtAny(this string self, ref Cursor cursor, params char[] values)
		{
			if (values == null || values.Length == 0)
			{
				throw new ArgumentNullException("values");
			}
			if (self == null || self.Length <= cursor)
			{
				throw ParseException.MakeFromExpected(cursor, values.ToString());
			}

			#region Switch values.Length

			switch (values.Length)
			{
				case 1:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					break;
				case 2:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					break;
				case 3:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					break;
				case 4:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					break;
				case 5:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					break;
				case 6:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					break;
				case 7:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					if (At(self, cursor, values[6]))
					{
						return values[6];
					}
					break;
				case 8:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					if (At(self, cursor, values[6]))
					{
						return values[6];
					}
					if (At(self, cursor, values[7]))
					{
						return values[7];
					}
					break;
				case 9:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					if (At(self, cursor, values[6]))
					{
						return values[6];
					}
					if (At(self, cursor, values[7]))
					{
						return values[7];
					}
					if (At(self, cursor, values[8]))
					{
						return values[8];
					}
					break;
				default:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					if (At(self, cursor, values[6]))
					{
						return values[6];
					}
					if (At(self, cursor, values[7]))
					{
						return values[7];
					}
					if (At(self, cursor, values[8]))
					{
						return values[8];
					}
					if (At(self, cursor, values[9]))
					{
						return values[9];
					}
					for (var i = 10; i < values.Length; i++)
					{
						if (At(self, cursor, values[i]))
						{
							return values[0];
						}
					}
					break;
			}

			#endregion

			throw ParseException.MakeFromExpected(cursor, values.ToString());
		}

		/// <summary>
		/// Examines if the cursor is positioned at one of a set of string values; throwing a ParseException otherwise.
		/// </summary>
		/// <param name="self"></param>
		/// <param name="cursor"></param>
		/// <param name="values"></param>
		/// <exception cref="ParseException">thrown if the cursor is not positioned at one of the strings given.</exception>
		/// <returns>the matching string</returns>
		public static string ExpectAtAny(this string self, ref Cursor cursor, params string[] values)
		{
			if (values == null)
			{
				throw new ArgumentNullException("values");
			}
			if (self == null || self.Length <= cursor || values.Length == 0)
			{
				throw ParseException.MakeFromExpected(cursor, values.ToString());
			}

			#region Switch values.Length

			switch (values.Length)
			{
				case 1:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					break;
				case 2:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					break;
				case 3:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					break;
				case 4:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					break;
				case 5:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					break;
				case 6:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					break;
				case 7:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					if (At(self, cursor, values[6]))
					{
						return values[6];
					}
					break;
				case 8:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					if (At(self, cursor, values[6]))
					{
						return values[6];
					}
					if (At(self, cursor, values[7]))
					{
						return values[7];
					}
					break;
				case 9:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					if (At(self, cursor, values[6]))
					{
						return values[6];
					}
					if (At(self, cursor, values[7]))
					{
						return values[7];
					}
					if (At(self, cursor, values[8]))
					{
						return values[8];
					}
					break;
				default:
					if (At(self, cursor, values[0]))
					{
						return values[0];
					}
					if (At(self, cursor, values[1]))
					{
						return values[1];
					}
					if (At(self, cursor, values[2]))
					{
						return values[2];
					}
					if (At(self, cursor, values[3]))
					{
						return values[3];
					}
					if (At(self, cursor, values[4]))
					{
						return values[4];
					}
					if (At(self, cursor, values[5]))
					{
						return values[5];
					}
					if (At(self, cursor, values[6]))
					{
						return values[6];
					}
					if (At(self, cursor, values[7]))
					{
						
						return values[7];
					}
					if (At(self, cursor, values[8]))
					{
						return values[8];
					}
					if (At(self, cursor, values[9]))
					{
						return values[9];
					}
					for (var i = 10; i < values.Length; i++)
					{
						if (At(self, cursor, values[i]))
						{
							return values[0];
						}
					}
					break;
			}

			#endregion

			throw ParseException.MakeFromExpected(cursor, values.ToString());
		}

		/// <summary>
		/// Gets the text from a sequence of captured tokens.
		/// </summary>
		/// <typeparam name="TKind">token kind TKind</typeparam>
		/// <param name="tokens">the tokens</param>
		/// <returns>the concatenated text of the tokens</returns>
		public static string GetText<TKind>(this IEnumerable<Token<TKind>> tokens)
			where TKind : struct
		{
			return GetText(tokens, false);
		}

		/// <summary>
		/// Gets the text from a sequence of captured tokens, optionally collapsing whitespace.
		/// </summary>
		/// <typeparam name="TKind">token kind TKind</typeparam>
		/// <param name="tokens">the tokens</param>
		/// <param name="collapseWhitespace">whether to collapse whitespace tokens</param>
		/// <returns>the concatenated text of the tokens</returns>
		public static string GetText<TKind>(this IEnumerable<Token<TKind>> tokens, bool collapseWhitespace)
			where TKind : struct
		{
			Contract.Requires(tokens != null, "tokens cannot be null");
			var buffer = new StringBuilder(400);
			if (collapseWhitespace)
			{
				var white = false;
				foreach (var t in tokens)
				{
					if (t.MetaKind == TokenMetaKind.Whitespace
						|| t.MetaKind == TokenMetaKind.Comment)
					{
						if (!white)
						{
							white = true;
							buffer.Append(' ');
						}
						continue;
					}
					white = false;
					buffer.Append(t.Capture.Value);
				}
			}
			else
			{
				foreach (var t in tokens)
				{
					buffer.Append(t.Capture.Value);
				}
			}
			return buffer.ToString();
		}

		/// <summary>
		/// Seeks the first index that matches a rule.
		/// </summary>
		/// <param name="self"></param>
		/// <param name="rule"></param>
		/// <returns>the offset where the rule first matches; otherwise -1</returns>
		public static int IndexOf(this string self, Match rule)
		{
			var c = new Cursor(0);
			for (; c < self.Length; c++)
			{
				if (rule.At(self, c))
				{
					return c.Offset;
				}
			}
			return -1;
		}

		/// <summary>
		/// Determines if the item is one of a list of items. Inteded to test enums.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="item"></param>
		/// <param name="values"></param>
		/// <returns></returns>
		public static bool IsOneOf<T>(this T item, params T[] values)
		{
			switch (values.Length)
			{
				case 1:
					return (EqualityComparer<T>.Default.Equals(item, values[0]));
				case 2:
					return (EqualityComparer<T>.Default.Equals(item, values[0])
						|| EqualityComparer<T>.Default.Equals(item, values[1]));
				case 3:
					return (EqualityComparer<T>.Default.Equals(item, values[0])
						|| EqualityComparer<T>.Default.Equals(item, values[1])
						|| EqualityComparer<T>.Default.Equals(item, values[2]));
				case 4:
					return (EqualityComparer<T>.Default.Equals(item, values[0])
						|| EqualityComparer<T>.Default.Equals(item, values[1])
						|| EqualityComparer<T>.Default.Equals(item, values[2])
						|| EqualityComparer<T>.Default.Equals(item, values[3]));
				case 5:
					return (EqualityComparer<T>.Default.Equals(item, values[0])
						|| EqualityComparer<T>.Default.Equals(item, values[1])
						|| EqualityComparer<T>.Default.Equals(item, values[2])
						|| EqualityComparer<T>.Default.Equals(item, values[3])
						|| EqualityComparer<T>.Default.Equals(item, values[4]));
				case 6:
					return (EqualityComparer<T>.Default.Equals(item, values[0])
						|| EqualityComparer<T>.Default.Equals(item, values[1])
						|| EqualityComparer<T>.Default.Equals(item, values[2])
						|| EqualityComparer<T>.Default.Equals(item, values[3])
						|| EqualityComparer<T>.Default.Equals(item, values[4])
						|| EqualityComparer<T>.Default.Equals(item, values[5]));
				case 7:
					return (EqualityComparer<T>.Default.Equals(item, values[0])
						|| EqualityComparer<T>.Default.Equals(item, values[1])
						|| EqualityComparer<T>.Default.Equals(item, values[2])
						|| EqualityComparer<T>.Default.Equals(item, values[3])
						|| EqualityComparer<T>.Default.Equals(item, values[4])
						|| EqualityComparer<T>.Default.Equals(item, values[5])
						|| EqualityComparer<T>.Default.Equals(item, values[6]));
				case 8:
					return (EqualityComparer<T>.Default.Equals(item, values[0])
						|| EqualityComparer<T>.Default.Equals(item, values[1])
						|| EqualityComparer<T>.Default.Equals(item, values[2])
						|| EqualityComparer<T>.Default.Equals(item, values[3])
						|| EqualityComparer<T>.Default.Equals(item, values[4])
						|| EqualityComparer<T>.Default.Equals(item, values[5])
						|| EqualityComparer<T>.Default.Equals(item, values[6])
						|| EqualityComparer<T>.Default.Equals(item, values[7])
						);
				case 9:
					return (EqualityComparer<T>.Default.Equals(item, values[0])
						|| EqualityComparer<T>.Default.Equals(item, values[1])
						|| EqualityComparer<T>.Default.Equals(item, values[2])
						|| EqualityComparer<T>.Default.Equals(item, values[3])
						|| EqualityComparer<T>.Default.Equals(item, values[4])
						|| EqualityComparer<T>.Default.Equals(item, values[5])
						|| EqualityComparer<T>.Default.Equals(item, values[6])
						|| EqualityComparer<T>.Default.Equals(item, values[7])
						|| EqualityComparer<T>.Default.Equals(item, values[8])
						);
				default:
					if (EqualityComparer<T>.Default.Equals(item, values[0])
						|| EqualityComparer<T>.Default.Equals(item, values[1])
						|| EqualityComparer<T>.Default.Equals(item, values[2])
						|| EqualityComparer<T>.Default.Equals(item, values[3])
						|| EqualityComparer<T>.Default.Equals(item, values[4])
						|| EqualityComparer<T>.Default.Equals(item, values[5])
						|| EqualityComparer<T>.Default.Equals(item, values[6])
						|| EqualityComparer<T>.Default.Equals(item, values[7])
						|| EqualityComparer<T>.Default.Equals(item, values[8])
						|| EqualityComparer<T>.Default.Equals(item, values[9]))
					{
						return true;
					}
					for (var i = 10; i < values.Length; i++)
					{
						if (EqualityComparer<T>.Default.Equals(item, values[i]))
						{
							return true;
						}
					}
					break;
			}
			return false;
		}

		/// <summary>
		/// Captures each sequence in the input that matches a rule.
		/// </summary>
		/// <param name="input"></param>
		/// <param name="rule"></param>
		/// <returns></returns>
		/// <remarks>Results do not overlap.</remarks>
		public static Cursor[] LocateAll(this string input, Match rule)
		{
			var result = new List<Cursor>();
			var c = new Cursor(0);
			while (c < input.Length)
			{
				var cc = c;
				if (rule.TryAt(input, ref c))
				{
					result.Add(cc);
				}
				else
				{
					c++;
				}
			}
			return result.ToArray();
		}


		/// <summary>
		/// Captures the first sequence matching the rule.
		/// </summary>
		/// <param name="input"></param>
		/// <param name="rule"></param>
		/// <returns></returns>
		public static Cursor LocateFirst(this string input, Match rule)
		{
			var c = new Cursor(0);
			while (c < input.Length)
			{
				var cc = c;
				if (rule.TryAt(input, ref c))
				{
					return cc;
				}
				c++;
			}
			return Cursor.Invalid;
		}

		/// <summary>
		/// Transforms whitespace into its human readable form.
		/// </summary>
		/// <param name="input"></param>
		/// <returns></returns>
		public static string MakeHumanReadableWhitespace(this string input)
		{
			return input.Replace("\r" // carriage return
													, @"\r")
									.Replace("\n" // line feed
													, @"\n")
									.Replace("\t" // tab
													, @"\t")
									.Replace("\b" // backspace				
													, @"\b")
									.Replace("\v" // vertical tab
													, @"\v")
									.Replace("\f" // form feed
													, @"\f")
									.Replace("\u0085" // next line
													, @"\u0085")
									.Replace("\u2028" // line separator
													, @"\u2028")
									.Replace("\u2029" // para separator
													, @"\u2029");
		}

		/// <summary>
		/// Transforms a whitespace character into human readable form.
		/// </summary>
		/// <param name="input"></param>
		/// <returns></returns>
		public static string MakeHumanReadableWhitespace(this char input)
		{
			switch (input)
			{
				case '\v':
					return @"\v";
				case '\r':
					return @"\r";
				case '\n':
					return @"\n";
				case '\t':
					return @"\t";
				case '\b':
					return @"\b";
				case '\f':
					return @"\f";
				case '\u0085':
					return @"\u0085";
				case '\u2028':
					return @"\u2028";
				case '\u2029':
					return @"\u2029";
				default:
					return input.ToString();
			}
		}

		/// <summary>
		///   Validates that the character at the cursor is the expected character
		///   but does not advance the cursor.
		/// </summary>
		/// <param name="self">input characters</param>
		/// <param name="cursor">reference to the parse cursor</param>
		/// <param name="ch">the expected character</param>
		/// <returns>
		///   the expected character (taken from the input) if the
		///   expected character is present; otherwise a ParseException is thrown
		/// </returns>
		/// <exception cref="ArgumentNullException">
		///   thrown if the <paramref name="self" /> is null.
		/// </exception>
		/// <exception cref="ArgumentOutOfRangeException">thrown if the cursor is beyond the end of the input.</exception>
		/// <exception cref="ParseException">thrown if the expected character is not present.</exception>
		public static char PeekExpectChar(this char[] self, Cursor cursor, char ch)
		{
			Contract.Requires(self != null, "input cannot be null");
			Contract.Requires(cursor <= self.Length, "cursor must be in the range of available characters");

			if (self[cursor] != ch)
			{
				throw ParseException.MakeFromExpected(cursor, ch.ToString());
			}

			return self[cursor];
		}

		/// <summary>
		///   Validates that the character at the cursor is one of the expected characters
		///   but does not advance the cursor.
		/// </summary>
		/// <param name="self">input characters</param>
		/// <param name="cursor">reference to the parse cursor</param>
		/// <param name="chars">an array containing the expected characters</param>
		/// <returns>
		///   the expected character at the cursor's position (taken from the input)
		///   if an expected character is present; otherwise a ParseException is thrown
		/// </returns>
		/// <exception cref="ArgumentNullException">
		///   thrown if the <paramref name="self" /> is null.
		/// </exception>
		/// <exception cref="ArgumentOutOfRangeException">thrown if the cursor is beyond the end of the input.</exception>
		/// <exception cref="ParseException">thrown if an expected character is not present at the cursor position.</exception>
		public static char PeekExpectChar(this char[] self, Cursor cursor, char[] chars)
		{
			Contract.Requires(self != null, "input cannot be null");
			Contract.Requires(cursor <= self.Length, "cursor must be in the range of available characters");

			for (var i = 0; i < chars.Length; i++)
			{
				if (self[cursor] == chars[i])
				{
					return self[cursor];
				}
			}
			throw new ParseException("Expected newline characters", cursor);
		}

		/// <summary>
		/// Wraps the string in single quotes, delimiting any single quotes already present in the string.
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		public static string Quote(this string value)
		{
			Contract.Requires(value != null);
			return Quote(value, '\'');
		}

		/// <summary>
		/// Wraps the string in quotes, delimiting any quotes already present in the string.
		/// </summary>
		/// <param name="value">the initial value</param>
		/// <param name="quoteChar">the quote character in use</param>
		/// <returns></returns>
		public static string Quote(this string value, char quoteChar)
		{
			Contract.Requires(value != null);
			var delimited = String.Concat('\\', quoteChar);

			return String.Concat(quoteChar, value.Replace(quoteChar.ToString(CultureInfo.InvariantCulture), delimited), quoteChar);
		}

		/// <summary>
		/// Advances the cursor beyond a line terminator. Works for '\r' and '\r\n' pairs.
		/// </summary>
		/// <param name="self"></param>
		/// <param name="cursor"></param>
		public static void SkipLineTerminator(this string self, ref Cursor cursor)
		{
			Contract.Requires(self != null, "input cannot be null");
			Contract.Requires(cursor <= self.Length, "cursor must be in the range of available characters");

			var ch = self.ExpectAtAny(ref cursor, Constants.LineTerminatorChars);

			// Check for multi-character line terminator (Windows)
			var cc = cursor + 1;
			if (ch == '\r' && self.Length > cc && self[cc] == '\n')
			{
				cursor = cursor.Newline(2);
			}
			else
			{
				cursor = cursor.Newline();
			}
		}

		/// <summary>
		///   Advances the cursor to the next line terminator or
		///   the end of input, whichever is first.
		/// </summary>
		/// <param name="self">input characters</param>
		/// <param name="cursor">input cursor</param>
		public static void SkipToLineTerminator(this string self, ref Cursor cursor)
		{
			Contract.Requires(self != null, "input cannot be null");
			Contract.Requires(cursor <= self.Length, "cursor must be in the range of available characters");

			while (cursor < self.Length
				&& !self.AtLineTerminator(cursor))
			{
				cursor++;
			}
		}

		/// <summary>
		///   Advances the cursor past any whitespace or comments.
		/// </summary>
		/// <param name="self">input characters</param>
		/// <param name="cursor">input cursor</param>
		/// <param name="singleLineCommentIndicator"></param>
		public static void SkipWhitespacAndComments(this string self, ref Cursor cursor, char[] singleLineCommentIndicator)
		{
			Contract.Requires(self != null, "input cannot be null");
			Contract.Requires(cursor <= self.Length, "cursor must be in the range of available characters");

			while (cursor < self.Length)
			{
				while (Char.IsWhiteSpace(self[cursor]))
				{
					if (self.AtLineTerminator(cursor))
					{
						self.SkipLineTerminator(ref cursor);
					}
					else
					{
						cursor++;
					}
				}
				if (self.At(cursor, singleLineCommentIndicator))
				{
					self.SkipToLineTerminator(ref cursor);
				}
				else
				{
					// Done skipping
					return;
				}
			}
		}

		/// <summary>
		///   Advances the cursor past any whitespace.
		/// </summary>
		/// <param name="self">input characters</param>
		/// <param name="cursor">input cursor</param>
		public static void SkipWhitespace(this string self, ref Cursor cursor)
		{
			Contract.Requires(self != null, "input cannot be null");
			Contract.Requires(cursor <= self.Length, "cursor must be in the range of available characters");

			if (self == null || self.Length <= cursor)
			{
				return;
			}
			while (AtWhitespace(self, cursor))
			{
				if (AtLineTerminator(self, cursor))
				{
					SkipLineTerminator(self, ref cursor);
				}
				else
				{
					cursor++;
				}
			}
		}

		/// <summary>
		/// Slices a sequence of characters from an array of characters.
		/// </summary>
		/// <param name="self"></param>
		/// <param name="offset"></param>
		/// <param name="length"></param>
		/// <returns></returns>
		public static char[] Slice(this char[] self, int offset, int length)
		{
			var result = new char[length];
			Array.Copy(self, offset, result, 0, length);
			return result;
		}

		/// <summary>
		/// Takes a sequence of digits from an array of characters.
		/// </summary>
		/// <param name="source"></param>
		/// <returns></returns>
		public static string TakeAnyDigits(this string source)
		{
			if (source == null)
			{
				return null;
			}
			var buffer = new StringBuilder(source.Length);
			for (var i = 0; i < source.Length; i++)
			{
				if (source.AtAny(i, Constants.Digits))
				{
					buffer.Append(source[i]);
				}
			}
			return (buffer.Length > 0) ? buffer.ToString() : null;
		}

		/// <summary>
		/// Tokenizes an input string.
		/// </summary>
		/// <typeparam name="TKind">Token kind TKind</typeparam>
		/// <param name="tokenizer">A tokenizer</param>
		/// <param name="input">the input</param>
		/// <returns>An enumeration over the tokens in the input string.</returns>
		public static IEnumerable<Token<TKind>> Tokenize<TKind>(this Tokenizer<TKind> tokenizer, string input)
			where TKind : struct
		{
			Contract.Requires(input != null, "input cannot be null");
			return tokenizer.Tokenize(input.ToCharArray());
		}

		/// <summary>
		/// Removes quote delimiters from the beginning and end of a string and removes the double-delimited quotes
		/// within.
		/// </summary>
		/// <param name="value"></param>
		/// <param name="quoteChar"></param>
		/// <returns></returns>
		public static string Unquote(this string value, char quoteChar)
		{
			Contract.Requires(value != null);
			Contract.Requires(value.Length > 0);

			if (value.Length > 1
				&& value[0] == quoteChar
				&& value[value.Length - 1] == quoteChar)
			{
				return value.Substring(1, value.Length - 2)
										.Replace(new String(quoteChar, 2), Convert.ToString(quoteChar));
			}
			return value;
		}

		/// <summary>
		/// Removes quote delimiters from the beginning and end of a string and removes the double-delimited quotes
		/// within. Detects the quote character based on the string's beginning character.
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		public static string UnquoteAny(this string value)
		{
			Contract.Requires(value != null);
			Contract.Requires(value.Length > 0);

			if (value.Length > 1)
			{
				if (value[0] == '\'' && value[value.Length - 1] == '\'')
				{
					return value.Substring(1, value.Length - 2)
											.Replace("''", "'");
				}
				else if (value[0] == '"' && value[value.Length - 1] == '"')
				{
					return value.Substring(1, value.Length - 2)
											.Replace("\"\"", "\"");
				}
			}
			return value;
		}
	}
}